﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using OSGeo.OGR;

namespace ZhZyz.Gdal
{
    public static class GdalService
    {
        #region 静态方法

        [DllImport("gdal204.dll", EntryPoint = "OGR_F_GetFieldAsBinary", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr OGR_F_GetFieldAsBinary(HandleRef handle, int index, out int byteCount);

        [DllImport("gdal204.dll", EntryPoint = "OGR_L_GetName", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr OGR_L_GetName(HandleRef handle);

        [DllImport("gdal204.dll", EntryPoint = "OGR_Fld_GetNameRef", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr OGR_Fld_GetNameRef(HandleRef handle);

        /// <summary>
        ///     读取图层名称
        /// </summary>
        /// <param name="layer">图层</param>
        /// <returns>图层名称</returns>
        public static string GetNameEx(this Layer layer)
        {
            var pName = OGR_L_GetName(Layer.getCPtr(layer));
            var bytes = Encoding.Unicode.GetBytes(Marshal.PtrToStringUni(pName)); //转成UNICODE编码
            var dec = Encoding.UTF8.GetString(bytes.TakeWhile(x => x != 0).ToArray()); //再转成UTF8
            return dec;
        }

        /// <summary>
        ///     获取字段名称
        /// </summary>
        /// <param name="fieldDefn">字段定义</param>
        /// <returns>字段名称</returns>
        public static string GetNameEx(this FieldDefn fieldDefn)
        {
            var pName = OGR_Fld_GetNameRef(FieldDefn.getCPtr(fieldDefn));
            var bytes = Encoding.Unicode.GetBytes(Marshal.PtrToStringUni(pName)); //转成UNICODE编码
            var dec = Encoding.UTF8.GetString(bytes.TakeWhile(x => x != 0).ToArray()); //再转成UTF8
            return dec;
        }

        public static string GetFieldValueEx(this Feature feature, int fieldIndex)
        {
            var pName = OGR_F_GetFieldAsBinary(Feature.getCPtr(feature), fieldIndex, out var byteCount);
            return GetStr(pName, byteCount);
        }

        /// <param name="fe">当前要素</param>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public static string getRightStr(Feature fe, int fieldIndex)
        {
            var byteCount = 0;
            var pNativeData = OGR_F_GetFieldAsBinary(Feature.getCPtr(fe), fieldIndex, out byteCount);
            return GetStr(pNativeData, byteCount);
        }

        private static string GetStr(IntPtr pNativeData, int byteCount)
        {
            if (pNativeData == IntPtr.Zero)
                return null;
            var byteArray = new byte[byteCount];
            Marshal.Copy(pNativeData, byteArray, 0, byteCount);

            // Console.WriteLine(byteCount);
            var str = Encoding.UTF8.GetString(byteArray);
            //Console.WriteLine(str);
            return str;
        }

        /// <summary>
        ///     获取数据源图层
        /// </summary>
        /// <param name="fn">数据源路径</param>
        /// <param name="fx">默认多面</param>
        /// <returns></returns>
        public static Dictionary<int, Layer> GetLayers(string fn, int fx = 6)
        {
            var driver = GetDrive(fn);
            if (driver == null) return null;
            var dataSource = driver.Open(fn, 0);

            if (dataSource == null) return null;

            var result = new Dictionary<int, Layer>();

            for (var i = 0; i < dataSource.GetLayerCount(); i++)
            {
                var layer = dataSource.GetLayerByIndex(i);

                if (layer.GetGeomType() == wkbGeometryType.wkbMultiPolygon) result.Add(i, layer);
            }
          
            return result;
        }

        public static DataSource OpenDataSource(string dataSource)
        {

            var driver = GetDrive(dataSource);
            if (driver == null) return null;
            if (driver.GetName() == "FileGDB")
            {
                if (!Directory.Exists(dataSource))
                    driver.CreateDataSource(dataSource, null);
                  //  Geodatabase.Create(dataSource);

            }
            else
            {
                if(!File.Exists(dataSource))
                    return driver.CreateDataSource(dataSource, null);
            }
            
            return driver.Open(dataSource, 0);
        }


        /// <summary>
        /// </summary>
        /// <param name="dataSource">数据源</param>
        /// <param name="layerName">图层名，不要中文，中文获取不到</param>
        /// <param name="geoType">图元类型，1点，2线，3面</param>
        /// <returns></returns>
        public static Layer GetLayer(DataSource dataSource, string layerName, int geoType,out bool isNewLayer)
        {
            var lay = dataSource.GetLayerByName(layerName);
            isNewLayer = false;

            if (lay == null)

            {
                var geometryType = wkbGeometryType.wkbPoint;
                switch (geoType)
                {
                    case 1:
                        geometryType = wkbGeometryType.wkbPoint;
                        break;
                    case 2:
                        geometryType = wkbGeometryType.wkbLineString;
                        break;
                    case 3:
                        geometryType = wkbGeometryType.wkbPolygon;
                        break;
                }
             
                lay = dataSource.CreateLayer(layerName, null, geometryType, null);
                isNewLayer = true;
            }

            return lay;
        }

        public static Geometry CreateGeometryFromJson(string json)
        {
            var geometry = Ogr.CreateGeometryFromJson(json);
            return geometry;
        }

        public static List<string> GetFields(Layer layer)
        {
            var defn = layer.GetLayerDefn();
            var lsfd = new List<string>();
            for (var i = 0; i < defn.GetFieldCount(); i++) lsfd.Add(defn.GetFieldDefn(i).GetNameEx());

            return lsfd;
        }

        private static Driver GetDrive(string fn)
        {
            switch (Path.GetExtension(fn).ToLower())
            {
                case ".gdb"://OGR 37: OpenFileGDB
                    return Ogr.GetDriverByName("FileGDB");
                case ".mdb"://OGR 33: PGeo
                    return Ogr.GetDriverByName("PGeo");
                case ".shp"://OGR 6: ESRI Shapefile
                    return Ogr.GetDriverByName("ESRI Shapefile");
                default:
                    return null;
            }
        }

        #endregion
    }
}